"""
__author__ = '霍格沃兹测试开发学社'
__desc__ = '更多测试开发技术探讨，请访问：https://ceshiren.com/t/topic/15860'
"""
import os
import subprocess
from dataclasses import asdict
from itertools import groupby

from csr_utils import log, get_root_dir

from dao import execution_dao, runner_dao
from orm_domain.execution import Execution
from orm_domain.job import Job


class ExecutionService:

    def save(self, job: Job, runner_id=None, remark=None):
        '''
        创建execution，若传入了runner_id 则只创建一个execution
        若未传入runner_id，则根据job_id获取所有的runner_id，创建所有runner_id的execution
        '''
        from service import project_service
        # 创建时从job中获取出job_id和execution_count
        job_id = job.id
        execution_count = job.execution_count
        # 获取项目sign 拼接完整的项目路径
        sign = project_service.get_by_id(job.project_id).sign
        workspace = os.sep.join([get_root_dir(), "cus_workspace", sign])
        # 从job中的关联信息中获取case对象
        cases_obj = self.__get_run_case_obj(job)
        # 若传入了runner_id 则只创建一个execution
        if runner_id:
            self.__run_case(job_id, workspace, execution_count, cases_obj, remark, runner_id)

        else:
            for run_cases_obj in cases_obj:
                self.__run_case(job_id, workspace, execution_count, run_cases_obj, remark)

    def __run_case(self, job_id, workspace, execution_count, cases_obj, remark, runner_id=None):
        run_cases_obj = {}
        run_cases_obj["runner_id"] = runner_id if runner_id else cases_obj.get("runner_id")
        run_cases_obj["workspace"] = workspace
        run_cases_obj["job_id"] = job_id
        run_cases_obj["testcase"] = sum([obj.get("testcase") for obj in cases_obj], []) if runner_id else cases_obj.get(
            "testcase")
        # 实例化 Execution，存库
        log.debug(
            f"使用job_id: {job_id},runner_id: {run_cases_obj.get('runner_id')}, execution_count: {execution_count}, remark: {remark}创建执行记录....")
        ec = Execution(job_id=job_id, runner_id=run_cases_obj.get('runner_id'), execution_count=execution_count,
                       remark=remark)
        execution_id = execution_dao.save(ec)
        run_cases_obj["execution_id"] = execution_id
        self.__run(run_cases_obj)

    def __get_run_case_obj(self, job: Job):
        # 从job_dao中获取suites
        suites = job.suites
        testcases = []
        # 把suites 中的testcases添加到列表中
        for suite in suites:
            testcases.extend(suite.testcases)
        # 获取job中包含的所有runner_id
        # runner_ids = [testcase.runner_id for testcase in testcases]
        run_case_lis = []
        testcases.sort(key=lambda x: x.runner_id)
        for dat, items in groupby(testcases, key=lambda x: x.runner_id):
            temp_dic = {}
            temp_dic["runner_id"] = dat
            temp_dic["testcase"] = [asdict(i) for i in items]
            run_case_lis.append(temp_dic)
        log.debug(f"获取到的run_case_lis为：{run_case_lis}")
        return run_case_lis

    def get_by_job_id(self, job_id):
        return execution_dao.get_list(job_id=job_id)

    def delete(self, _id):
        ec = execution_dao.get(id=_id)
        return execution_dao.delete(ec)

    # def rebuild(self, job_id):
    #     self.__run(job_id)

    def __run(self, obj):
        """
        只有新创建一个执行记录的时候才会调用此方法
        通过rebuild 和 save 调用，不对外暴露
        """
        # 通过runner_id获取runner对象
        runner_obj = runner_dao.get_by_id(obj.get("runner_id"))
        # 从runner对象中获取命令和参数信息
        command = runner_obj.command or " "
        command_config = runner_obj.command_config or " "
        # 从用例列表中获取所有的用例字典
        testcases = obj.get("testcase")
        log.debug(f"case集为：{list(testcases)}")
        # 得到所有的用例的uid用于执行
        case_uids = [case.get("uid") or " " for case in testcases]
        # 拼接执行命令
        final_command = command + " " + " ".join(
            case_uids) + " " + command_config + f"=./report_{obj.get('job_id')}_{obj.get('execution_id')}.xml"
        log.debug(f"执行的命令为：{final_command}")
        sub = subprocess.Popen(final_command, cwd=obj.get("workspace"), stdin=subprocess.PIPE, shell=True, bufsize=4096)
        return sub.returncode
